package com.interwater.coffeecoupon;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;

import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentFilter;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.MifareClassic;
import android.nfc.tech.MifareUltralight;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Parcelable;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.widget.TextView;

import com.interwater.ajax.AndroidAjax;
import com.interwater.ajax.ReRequestOptions;
import com.interwater.coffeecoupon.base.BaseActivity;
import com.interwater.coffeecoupon.base.NativeCall;
import com.interwater.coffeecoupon.common.Constants;
import com.interwater.coffeecoupon.common.LogUtil;
import com.interwater.coffeecoupon.nfc.NdefMessageParser;
import com.interwater.coffeecoupon.nfc.record.ParsedNdefRecord;

public class WebViewActivity extends BaseActivity {
	private final String Tag = this.getClass().getSimpleName();
	
	// UI 를 사용 하므로 defalut 로 handler 를  setting 해주어야 한다.
	private Handler mHandler;
	
	private NfcAdapter mAdapter;
    private PendingIntent mPendingIntent;
    private NdefMessage mNdefPushMessage;
    
    private static final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat();
	/**
	 * web view contents
	 */
	public WebView mWebView;
	
	// New methods in Android 2.3.3
    private static Method sAdapter_enableForegroundDispatch;
    private static Method sAdapter_enableForegroundNdefPush;
    private static Method sAdapter_disableForegroundDispatch;
    private static Method sAdapter_disableForegroundNdefPush;
    static {
        try {
            sAdapter_enableForegroundDispatch = NfcAdapter.class.getMethod("enableForegroundDispatch", new Class[] {
                    Activity.class, PendingIntent.class, IntentFilter[].class, String[][].class });
            sAdapter_enableForegroundNdefPush = NfcAdapter.class.getMethod("enableForegroundNdefPush", new Class[] {
                    Activity.class, NdefMessage.class });
            sAdapter_disableForegroundDispatch = NfcAdapter.class.getMethod("disableForegroundDispatch",
                    new Class[] { Activity.class });
            sAdapter_disableForegroundNdefPush = NfcAdapter.class.getMethod("disableForegroundNdefPush",
                    new Class[] { Activity.class });
        } catch (NoSuchMethodException e) {
            // failure, i.e Android 2.3-2.3.2
        }
    }
	
    
	@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        // nfc 관련 
        mAdapter = NfcAdapter.getDefaultAdapter(this);
        if (mAdapter == null) {
            LogUtil.error("mAdapter is null");
        }

        mPendingIntent = PendingIntent.getActivity(this, 0,
                new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
        mNdefPushMessage = new NdefMessage(new NdefRecord[] { newTextRecord("Message from NFC Reader :-)",
                Locale.ENGLISH, true) });
        
        
		mWebView = (WebView) findViewById(R.id.webview);
		WebSettings webSettings = mWebView.getSettings();
		webSettings.setSavePassword(false);
		webSettings.setSaveFormData(false);
		webSettings.setJavaScriptEnabled(true);
		webSettings.setSupportZoom(false);
        
		
		
		ReRequestOptions rro = new ReRequestOptions();
		// re RequestCount 10 번
		rro.setReRequestCount(10);
		// SuspendedSleepSec 5초
		rro.setSuspendedSleepSec(5);
		// Timeout 30초 
		rro.setTimeout(30);
		
		// 일반 native call 처리 
		mWebView.addJavascriptInterface(new NativeCall(mWebView), "nativeCall");
		
		// ajax 를 호출 할 base url / 뒤에 경로 값만 input 으로 넣어 주면 된다. 
		String url = "http://coffeServer.html/";
		// ajax 처리
		mWebView.addJavascriptInterface(new AndroidAjax(mWebView, url, rro), "coffeServerCall");
		
		// asset 사용 여부 
		if (Constants.IS_USE_ASSET) {
			mWebView.loadUrl("file:///android_asset/demo.html");
		} else {
			// 서버 web page 경로 
			
		}
        
		/** html file 에서 사용 할 sample
         * function asyncCall() { 
				window.demo.asyncCall("appcross/rank/search"
						, "{\"gubun\":\"1\",\"cate\":\"T\",\"free\":\"Y\",\"cpage\":\"1\",\"rows\":\"10\",\"user_no\":\"1000062\"}"
						, "callbacks", "error");
			}
         * function syncCall() { 
				window.demo.syncCall("appcross/rank/search"
						, "{\"gubun\":\"1\",\"cate\":\"T\",\"free\":\"Y\",\"cpage\":\"1\",\"rows\":\"10\",\"user_no\":\"1000062\"}"
						);
				}
         */
    }
	
	private NdefRecord newTextRecord(String text, Locale locale, boolean encodeInUtf8) {
        byte[] langBytes = locale.getLanguage().getBytes(Charset.forName("US-ASCII"));

        Charset utfEncoding = encodeInUtf8 ? Charset.forName("UTF-8") : Charset.forName("UTF-16");
        byte[] textBytes = text.getBytes(utfEncoding);

        int utfBit = encodeInUtf8 ? 0 : (1 << 7);
        char status = (char) (utfBit + langBytes.length);

        byte[] data = new byte[1 + langBytes.length + textBytes.length];
        data[0] = (byte) status;
        System.arraycopy(langBytes, 0, data, 1, langBytes.length);
        System.arraycopy(textBytes, 0, data, 1 + langBytes.length, textBytes.length);

        return new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], data);
    }

	/**
	 * Activity의 UI 변경이 존재 할 경우 아래 handler 를 setting 함으로써..
	 * handler class 를 만드는 것을 추천합니다. 
	 */
	@Override
	public Handler setHanlder(Activity activity) {
		return new Handler(){
			public void handleMessage(Message msg) {
				LogUtil.v(Tag, "handling");
			}
		};
	}
	
	/**
	 * nfc 태크를 읽었을 때에 호출 되는 intent
	 */
	@Override
    public void onNewIntent(Intent intent) {
        setIntent(intent);
        LogUtil.v(Tag,  intent.getAction());
        LogUtil.v(Tag,  "태그 읽었어요 " );
        resolveIntent(intent);
    }

	@Override
	protected void onResume() {
		super.onResume();
        if (mAdapter != null) {
            if (!mAdapter.isEnabled()) {
                LogUtil.error("mAdapter is not enabled");
            }
            try {
                sAdapter_enableForegroundDispatch.invoke(mAdapter, this, mPendingIntent, null, null);
                sAdapter_enableForegroundNdefPush.invoke(mAdapter, this, mNdefPushMessage);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
	}

	@Override
	protected void onPause() {
		super.onPause();
        if (mAdapter != null) {
            try {
                sAdapter_disableForegroundDispatch.invoke(mAdapter, this);
                sAdapter_disableForegroundNdefPush.invoke(mAdapter, this);
            } catch (Exception e) {
                // ignore
            }
        }
	}
	
	private void resolveIntent(Intent intent) {
        String action = intent.getAction();
        if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action) || NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
            Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
            NdefMessage[] msgs;
            if (rawMsgs != null) {
                msgs = new NdefMessage[rawMsgs.length];
                for (int i = 0; i < rawMsgs.length; i++) {
                    msgs[i] = (NdefMessage) rawMsgs[i];
                }
            } else {
                // Unknown tag type
                byte[] empty = new byte[0];
                byte[] id = intent.getByteArrayExtra(NfcAdapter.EXTRA_ID);
                Parcelable tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
                byte[] payload = null;
                try {
                    payload = dumpTagData(tag).getBytes();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
                NdefRecord record = new NdefRecord(NdefRecord.TNF_UNKNOWN, empty, id, payload);
                NdefMessage msg = new NdefMessage(new NdefRecord[] { record });
                msgs = new NdefMessage[] { msg };
            }
            // Setup the views
            if(msgs == null || msgs.length == 0)
            	return ;
            
            List<ParsedNdefRecord> records = NdefMessageParser.parse(msgs[0]);
            final int size = records.size();
            Date now = new Date();
            for (int i = 0; i < size; i++) {
                // time set 
            	LogUtil.v(Tag, TIME_FORMAT.format(now));
                ParsedNdefRecord record = records.get(i);
            }
        }
    }
	
	private Object translateTarget(String target) {
        if ("iso14443_3a".equals(target))
            return "ISO 14443-3A";
        else if ("iso14443_3b".equals(target))
            return "ISO 14443-3B";
        else if ("iso14443_4".equals(target))
            return "ISO 14443-4";
        else if ("iso15693".equals(target))
            return "ISO 15693 (RFID)";
        else if ("jis_x_6319_4".equals(target))
            return "JIS X-6319-4 (FeliCa)";
        else if ("other".equals(target))
            return "Unknown";

        return target;
    }
	
	/**
     * The reflection stuff in this method is copied from some Japanies site for backwards compatibility with Android
     * 2.3-2.3.2.
     */
    private String dumpTagData(Parcelable p) throws SecurityException, IllegalArgumentException, IllegalAccessException {
        StringBuilder sb = new StringBuilder();
        Field f = null;
        Class<?> tagClass = p.getClass();

        try {
            f = tagClass.getDeclaredField("mId");
            f.setAccessible(true);
            byte[] mId = (byte[]) f.get(p);
            sb.append("Tag ID (hex): ").append(getHex(mId)).append("\n");
            sb.append("Tag ID (dec): ").append(getDec(mId)).append("\n");
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }

        try {
            f = tagClass.getDeclaredField("mRawTargets");
            f.setAccessible(true);
            String[] mRawTargets = (String[]) f.get(p);
            sb.append("Targets: ");
            if (mRawTargets.length == 1)
                sb.append(translateTarget(mRawTargets[0]));
            else {
                for (String s : mRawTargets) {
                    sb.append(translateTarget(s)).append(", ");
                }
            }
            sb.append("\n");
        } catch (NoSuchFieldException e) {
            String prefix = "android.nfc.tech.";
            Tag tag = (Tag) p;
            sb.append("Technologies: ");
            for (String tech : tag.getTechList()) {
                sb.append(tech.substring(prefix.length()));
                sb.append(", ");
            }
            sb.delete(sb.length() - 2, sb.length());
            sb.append('\n');
            for (String tech : tag.getTechList()) {
                if (tech.equals(MifareClassic.class.getName())) {
                    MifareClassic mifareTag = MifareClassic.get(tag);
                    String type = "Unknown";
                    switch (mifareTag.getType()) {
                    case MifareClassic.TYPE_CLASSIC:
                        type = "Classic";
                        break;
                    case MifareClassic.TYPE_PLUS:
                        type = "Plus";
                        break;
                    case MifareClassic.TYPE_PRO:
                        type = "Pro";
                        break;
                    }
                    sb.append("Mifare Classic type: ");
                    sb.append(type);
                    sb.append('\n');

                    sb.append("Mifare size: ");
                    sb.append(mifareTag.getSize() + " bytes");
                    sb.append('\n');

                    sb.append("Mifare sectors: ");
                    sb.append(mifareTag.getSectorCount());
                    sb.append('\n');

                    sb.append("Mifare blocks: ");
                    sb.append(mifareTag.getBlockCount());
                    sb.append('\n');
                }

                if (tech.equals(MifareUltralight.class.getName())) {
                    MifareUltralight mifareUlTag = MifareUltralight.get(tag);
                    String type = "Unknown";
                    switch (mifareUlTag.getType()) {
                    case MifareUltralight.TYPE_ULTRALIGHT:
                        type = "Ultralight";
                        break;
                    case MifareUltralight.TYPE_ULTRALIGHT_C:
                        type = "Ultralight C";
                        break;
                    }
                    sb.append("Mifare Ultralight type: ");
                    sb.append(type);
                    sb.append('\n');
                }
            }
        }

        try {
            f = tagClass.getDeclaredField("mPollBytes");
            f.setAccessible(true);
            byte[] mPollBytes = (byte[]) f.get(p);
            if (mPollBytes != null && mPollBytes.length > 0)
                sb.append("Poll (hex): ").append(getHex(mPollBytes)).append("\n");
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }

        try {
            f = tagClass.getDeclaredField("mActivationBytes");
            f.setAccessible(true);
            byte[] mActivationBytes = (byte[]) f.get(p);
            if (mActivationBytes != null && mActivationBytes.length > 0)
                sb.append("Activation (hex): ").append(getHex(mActivationBytes));
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }

        return sb.toString();
    }
    
    private String getHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            int b = bytes[i] & 0xff;
            if (b < 0x10)
                sb.append('0');
            sb.append(Integer.toHexString(b));
            if (i != bytes.length - 1) {
                sb.append(" ");
            }
        }
        return sb.toString();
    }

    private long getDec(byte[] bytes) {
        long result = 0;
        long factor = 1;
        for (int i = 0; i < bytes.length; ++i) {
            long value = bytes[i] & 0xffl;
            result += value * factor;
            factor *= 256l;
        }
        return result;
    }
}
